package com.xfashionbrand.ui.posts.services;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;

import com.xfashionbrand.WordPress;
import com.xfashionbrand.models.Blog;
import com.xfashionbrand.util.AppLog;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlrpc.android.ApiHelper;
import org.xmlrpc.android.XMLRPCClientInterface;
import org.xmlrpc.android.XMLRPCException;
import org.xmlrpc.android.XMLRPCFactory;
import org.xmlrpc.android.XMLRPCFault;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import de.greenrobot.event.EventBus;

/**
 * service which retrieves posts for the post list
 */

public class PostUpdateService extends Service {

    private static final String ARG_BLOG_ID = "blog_id";
    private static final String ARG_LOAD_MORE = "load_more";
    private static final String ARG_IS_PAGE = "is_page";
    private static final String ARG_IS_PRODUCT = "is_product";
    private static final String ARG_PRODUCT_TYPE = "product_type";

    private static final int NUM_POSTS_TO_REQUEST = 20;

    /*
     * fetch posts/pages in a specific blog
     */
    public static void startServiceForBlog(Context context, int blogId, boolean isPage, boolean loadMore) {
        Intent intent = new Intent(context, PostUpdateService.class);
        intent.putExtra(ARG_BLOG_ID, blogId);
        intent.putExtra(ARG_IS_PAGE, isPage);
        intent.putExtra(ARG_LOAD_MORE, loadMore);
        context.startService(intent);
    }

    public static void startServiceForBlog(Context context, int blogId, boolean isPage, boolean loadMore, boolean isProduct, String productType) {
        Intent intent = new Intent(context, PostUpdateService.class);
        intent.putExtra(ARG_BLOG_ID, blogId);
        intent.putExtra(ARG_IS_PAGE, isPage);
        intent.putExtra(ARG_LOAD_MORE, loadMore);
        intent.putExtra(ARG_IS_PRODUCT, isProduct);
        intent.putExtra(ARG_PRODUCT_TYPE, productType);
        context.startService(intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        AppLog.i(AppLog.T.POSTS, "PostUpdateService > created");
    }

    @Override
    public void onDestroy() {
        AppLog.i(AppLog.T.POSTS, "PostUpdateService > destroyed");
        super.onDestroy();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(final Intent intent, int flags, int startId) {
        if (intent == null) return START_NOT_STICKY;

        new Thread() {
            @Override
            public void run() {
                int blogId = intent.getIntExtra(ARG_BLOG_ID, 0);
                boolean isPage = intent.getBooleanExtra(ARG_IS_PAGE, false);
                boolean loadMore = intent.getBooleanExtra(ARG_LOAD_MORE, false);
                boolean isProduct = intent.getBooleanExtra(ARG_IS_PRODUCT, false);
                String productType = intent.getStringExtra(ARG_PRODUCT_TYPE);
                if (!isProduct)
                    fetchPostsInBlog(blogId, isPage, loadMore);
                else
                    fetchProductsInBlog(blogId, isPage, loadMore, productType);
            }
        }.start();

        return START_NOT_STICKY;
    }

    private void fetchPostsInBlog(int blogId, boolean isPage, boolean loadMore) {
        Blog blog = WordPress.getBlog(blogId);
        if (blog == null) {
            return;
        }
        String postTypeSub = "post";

        XMLRPCClientInterface client = XMLRPCFactory.instantiate(
                blog.getUri(),
                blog.getHttpuser(),
                blog.getHttppassword());

        int numPostsToRequest;
        if (loadMore) {
            int numExisting = WordPress.wpDB.getUploadedCountInBlog(blogId, isPage, postTypeSub);
            numPostsToRequest = numExisting + NUM_POSTS_TO_REQUEST;
        } else {
            numPostsToRequest = NUM_POSTS_TO_REQUEST;
        }

        Object[] result;
        Object[] xmlrpcParams = {
                blog.getRemoteBlogId(),
                blog.getUsername(),
                blog.getPassword(),
                numPostsToRequest};

        PostEvents.RequestPosts event = new PostEvents.RequestPosts(blogId, isPage);
        try {
            boolean canLoadMore;

            result = (Object[]) client.call(isPage ? ApiHelper.Methods.GET_PAGES : "metaWeblog.getRecentPosts", xmlrpcParams);
            if (result != null && result.length > 0) {
                canLoadMore = true;

                // If we're loading more posts, only save the posts at the end of the array.
                // NOTE: Switching to wp.getPosts wouldn't require janky solutions like this
                // since it allows for an offset parameter.
                int startPosition = 0;
                if (loadMore && result.length > NUM_POSTS_TO_REQUEST) {
                    startPosition = result.length - NUM_POSTS_TO_REQUEST;
                }

                List<Map<?, ?>> postsList = new ArrayList<>();
                for (int ctr = startPosition; ctr < result.length; ctr++) {
                    Map<?, ?> postMap = (Map<?, ?>) result[ctr];
                    postsList.add(postMap);
                }

                if (!loadMore) {
                    WordPress.wpDB.deleteUploadedPosts(blogId, isPage, postTypeSub);
                }
//                AppLog.i(AppLog.T.API, postsList.toString());
                WordPress.wpDB.savePosts(postsList, blogId, isPage, false, postTypeSub);
            } else {
                canLoadMore = false;
            }

            event.setCanLoadMore(canLoadMore);

        } catch (XMLRPCException | IOException | XmlPullParserException e){
            AppLog.e(AppLog.T.POSTS, e);
            ApiHelper.ErrorType errorType;
            if (e instanceof XMLRPCFault) {
                if (((XMLRPCFault)(e)).getFaultCode() == 401) {
                    errorType = ApiHelper.ErrorType.UNAUTHORIZED;
                } else {
                    errorType = ApiHelper.ErrorType.NETWORK_XMLRPC;
                }
            } else {
                errorType = ApiHelper.ErrorType.INVALID_RESULT;
            }

            event.setErrorType(errorType);
        }
        event.setPostTypeSub(postTypeSub);

        EventBus.getDefault().post(event);
    }

    private void fetchProductsInBlog(int blogId, boolean isPage, boolean loadMore, String productType) {
        Blog blog = WordPress.getBlog(blogId);
        if (blog == null) {
            return;
        }

        XMLRPCClientInterface client = XMLRPCFactory.instantiate(
                blog.getUri(),
                blog.getHttpuser(),
                blog.getHttppassword());

        String postTypeSub = "product_" + productType;
        int numPostsToRequest;
        if (loadMore) {
            int numExisting = WordPress.wpDB.getUploadedCountInBlog(blogId, isPage, postTypeSub);
            numPostsToRequest = numExisting + NUM_POSTS_TO_REQUEST;
        } else {
            numPostsToRequest = NUM_POSTS_TO_REQUEST;
        }

        Map<Object, Object> map = new Hashtable<>();
        map.put("number", numPostsToRequest);
        map.put("post_type", "product");
        map.put("product_type", productType);
        Object[] result;
        Object[] xmlrpcParams = {
                blog.getRemoteBlogId(),
                blog.getUsername(),
                blog.getPassword(),
                map};

        PostEvents.RequestPosts event = new PostEvents.RequestPosts(blogId, isPage);
        try {
            boolean canLoadMore;

            result = (Object[]) client.call(isPage ? ApiHelper.Methods.GET_PAGES : "wp.getPosts", xmlrpcParams);
            if (result != null && result.length > 0) {
                canLoadMore = true;

                // If we're loading more posts, only save the posts at the end of the array.
                // NOTE: Switching to wp.getPosts wouldn't require janky solutions like this
                // since it allows for an offset parameter.
                int startPosition = 0;
                if (loadMore && result.length > NUM_POSTS_TO_REQUEST) {
                    startPosition = result.length - NUM_POSTS_TO_REQUEST;
                }

                List<Map<?, ?>> postsList = new ArrayList<>();
                for (int ctr = startPosition; ctr < result.length; ctr++) {
                    Map<?, ?> postMap = (Map<?, ?>) result[ctr];
                    Map<Object, Object> newMap = new HashMap<>();
                    newMap.put("postid", postMap.get("post_id"));
                    newMap.put("title", postMap.get("post_title"));

                    newMap.put("dateCreated", postMap.get("post_date"));
                    newMap.put("date_created_gmt", postMap.get("post_date_gmt"));
                    newMap.put("link", postMap.get("link"));
                    newMap.put("permaLink", postMap.get("guid"));
//                    newMap.put("categories", postMap.get("post_date"));
                    newMap.put("custom_fields", postMap.get("custom_fields"));
                    newMap.put("mt_excerpt", postMap.get("post_excerpt"));
                    newMap.put("mt_text_more", postMap.get("post_content"));
                    newMap.put("wp_post_thumbnail", ((Map<?, ?>) postMap.get("post_thumbnail")).get("attachment_id"));
                    newMap.put("mt_allow_comments", postMap.get("comment_status"));
                    newMap.put("wp_password", postMap.get("post_password"));
                    newMap.put("wp_author_id", postMap.get("post_author"));
                    newMap.put("mt_allow_pings", postMap.get("ping_status"));
                    newMap.put("post_status", postMap.get("post_status"));
                    newMap.put("dateCreated", postMap.get("post_date"));

//                    wp_slug
//                    wp_author_display
//                    wp_page_parent_title
//                    mt_keywords

                    newMap.put("userid", postMap.get("post_author"));
                    newMap.put("wp_page_parent_id", postMap.get("post_parent"));
                    newMap.put("wp_post_format", postMap.get("post_format"));

                    postsList.add(newMap);
                }

                if (!loadMore) {
                    WordPress.wpDB.deleteUploadedPosts(blogId, isPage, postTypeSub);
                }
                WordPress.wpDB.savePosts(postsList, blogId, isPage, false, postTypeSub);
            } else {
                canLoadMore = false;
            }

            event.setCanLoadMore(canLoadMore);

        } catch (XMLRPCException | IOException | XmlPullParserException e){
            AppLog.e(AppLog.T.POSTS, e);
            ApiHelper.ErrorType errorType;
            if (e instanceof XMLRPCFault) {
                if (((XMLRPCFault)(e)).getFaultCode() == 401) {
                    errorType = ApiHelper.ErrorType.UNAUTHORIZED;
                } else {
                    errorType = ApiHelper.ErrorType.NETWORK_XMLRPC;
                }
            } else {
                errorType = ApiHelper.ErrorType.INVALID_RESULT;
            }

            event.setErrorType(errorType);
        }
        event.setPostTypeSub(postTypeSub);

        EventBus.getDefault().post(event);
    }
}
